package com.zj.java21demo.utils;

//import com.zj.java21demo.entity.TUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @Description Jwt util
 * @Version 1.0.0
 * @Date 2024/2/28 13:41
 * @Created by zhangjun
 */

@Component
public class JwtTokenByJdk21Util {
    /**
     * 加密盐值or密钥
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     * 过期时间
     */
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * Token刷新时间
     */
    @Value("${jwt.refreshExpiration}")
    private long refreshExpiration;

    /**
     * 加密算法
     */
    private final SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;

    /**
     * 秘钥的实例key
     */
//    private final SecretKey KEY = Keys.hmacShaKeyFor(secret.getBytes());


    /**
     * 从Token中获取主题数据
     *
     * @param token Token
     * @return String
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 从Token中获取数据,受限于Claims
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 自定义提取任意数据
     * @param token
     * @param var1
     * @param var2
     * @return
     * @param <T>
     */
    public <T> T extractClaim(String token, String var1, Class<T> var2) {
        final Claims claims = extractAllClaims(token);
        return claims.get(var1, var2);
    }

    /**
     * 生成Token无额外信息，自定义用户类型!!!
     */
    public String generateToken(UserDetails userDetails) {
        return generateToken(new HashMap<>(), userDetails);
    }

    /**
     * 生成Token,有额外信息, 自定义用户类型!!!
     *
     * @param extraClaims 额外的数据
     * @param userDetails 用户信息
     * @return String
     */
    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails
    ) {
        return buildToken(extraClaims, userDetails, expiration);
    }

    /**
     * 生成刷新用的Token
     *
     * @param userDetails 用户信息
     * @return String
     */
    public String generateRefreshToken(
            UserDetails userDetails
    ) {
        return buildToken(new HashMap<>(), userDetails, refreshExpiration);
    }

    /**
     * 构建Token方法, 自定义用户信息
     *
     * @param extraClaims 额外信息
     * @param userDetails //用户信息
     * @param expiration  //失效时间
     * @return String
     */
    private String buildToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails,
            long expiration
    ) {
        return Jwts
                .builder()
                .claims(extraClaims)//body
                .subject(userDetails.getUsername())//核心数据，比如账号名、用户名称
                .issuedAt(new Date(System.currentTimeMillis()))//设置发布时间
                .expiration(new Date(System.currentTimeMillis() + expiration)) //设置过期时间
                .signWith(getSignInKey2(), ALGORITHM)//设置摘要算法
                .compact();
    }

    /**
     * 验证Token是否有效
     *
     * @param token       Token
     * @param userDetails 用户信息
     * @return boolean
     */
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
    }

    /**
     * 判断Token是否过去
     */
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * 从Token中获取失效时间
     */
    private Date extractExpiration(String token) {
        //通用方法,传入一个Function,返回一个T
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 从Token中获取所有数据
     */
    private Claims extractAllClaims(String token) {
        return Jwts
                .parser()
                .verifyWith(getSignInKey2())
                .build()
                .parseSignedClaims(token)
                .getPayload();//todo
    }

    /**
     * 获取签名Key,废弃！
     * Token 加密解密使用
     */
//    private Key getSignInKey() {
//        byte[] keyBytes = Decoders.BASE64.decode(secret);
//        return Keys.hmacShaKeyFor(keyBytes);
//    }

    private SecretKey getSignInKey2() {
        return Keys.hmacShaKeyFor(secret.getBytes());
    }
}
